﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Configuration;

namespace BizElements.Core.ProviderComponents
{
    /// <summary>Creates objects that implement interfaces declared in <b>BizElements.Core.ProviderComponents</b> namespace.</summary>
    static class DbmsComponentFactory
    {
        static readonly Dictionary<string, string> ProviderAssemblies = new Dictionary<string, string>()
        {
            {"SqlServer_7",     ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.SqlServer_7"]   	?? "BizElements.Core"},
            {"SqlServer_2000",  ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.SqlServer_2000"]	?? "BizElements.Core"},
            {"SqlServer_2005",  ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.SqlServer_2005"]	?? "BizElements.Core"},
            {"SqlServer_2008",  ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.SqlServer_2008"]	?? "BizElements.Core"},
            {"Oracle_9i",       ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.Oracle_9i"]     	?? "BizElements.Core.Oracle"},
            {"Oracle_10g",      ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.Oracle_10g"]    	?? "BizElements.Core.Oracle"},
            {"Oracle_11g",      ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.Oracle_11g"]    	?? "BizElements.Core.Oracle"},
            {"OleGeneric",      ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.OleGeneric"]    	?? "BizElements.Core"},
            {"OdbcGeneric",     ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.OdbcGeneric"]   	?? "BizElements.Core"},
            {"MySql_5",         ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.MySql_5"]       	?? "BizElements.Core.MySql"},
            {"PostgreSql_9",    ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.PostgreSql_9"]  	?? "BizElements.Core.PostgreSql"},
            {"Firebird_2",      ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.Firebird_2"]    	?? "BizElements.Core.Firebird"},
            {"SQLite_3",        ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.SQLite_3"]      	?? "BizElements.Core.SQLite"},
            {"SqlServerCe_4",   ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.SqlServerCe_4"] 	?? "BizElements.Core.SqlServerCe"},
            {"DB2_9",           ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.DB2_9"]         	?? "BizElements.Core.DB2"},
            {"Custom",          ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderAssembly.Custom"]}
        };

        static readonly Dictionary<string, string> ProviderClasses = new Dictionary<string, string>()
        {
            {"SqlServer_7",     ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.SqlServer_7"]     ?? "BizElements.Core.SqlServer.SqlServer7And2000ComponentProvider"},
            {"SqlServer_2000",  ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.SqlServer_2000"]  ?? "BizElements.Core.SqlServer.SqlServer7And2000ComponentProvider"},
            {"SqlServer_2005",  ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.SqlServer_2005"]  ?? "BizElements.Core.SqlServer.SqlServer2005ComponentProvider"},
            {"SqlServer_2008",  ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.SqlServer_2008"]  ?? "BizElements.Core.SqlServer.SqlServer2005ComponentProvider"},
            {"Oracle_9i",       ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.Oracle_9i"]       ?? "BizElements.Core.Oracle.Oracle9And10ComponentProvider"},
            {"Oracle_10g",      ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.Oracle_10g"]      ?? "BizElements.Core.Oracle.Oracle9And10ComponentProvider"},
            {"Oracle_11g",      ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.Oracle_11g"]      ?? "BizElements.Core.Oracle.Oracle9And10ComponentProvider"},
            {"OleGeneric",      ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.OleGeneric"]      ?? "BizElements.Core.OleDb.OleDbComponentProvider"},
            {"OdbcGeneric",     ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.OdbcGeneric"]     ?? "BizElements.Core.Odbc.OdbcComponentProvider"},
            {"MySql_5",         ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.MySql_5"]         ?? "BizElements.Core.MySql.MySqlComponentProvider"},
            {"PostgreSql_9",    ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.PostgreSql_9"]    ?? "BizElements.Core.PostgreSql.PostgreSqlComponentProvider"},
            {"Firebird_2",      ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.Firebird_2"]      ?? "BizElements.Core.Firebird.FirebirdComponentProvider"},
            {"SQLite_3",        ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.SQLite_3"]        ?? "BizElements.Core.SQLite.SQLiteComponentProvider"},
            {"SqlServerCe_4",   ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.SqlServerCe_4"]   ?? "BizElements.Core.SqlServerCe.SqlServerCeComponentProvider"},
            {"DB2_9",           ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.DB2_9"]           ?? "BizElements.Core.DB2.DB2ComponentProvider"},
            {"Custom",          ConfigurationManager.AppSettings["BizElements.Core.DbmsComponentProviderClass.Custom"]}
        };

        // Providers are initialized via reflection on first request and live singleton instances are cached in dictionary.
        static readonly Dictionary<DbmsType, IDbmsComponentProvider> Providers = new Dictionary<DbmsType, IDbmsComponentProvider>();

        /// <summary>Gets component that implements the defined interface for the specifed DBMS.</summary>
        /// <param name="dbms">Target DBMS. Assembly and provider class must be registered.</param>
        /// <typeparam name="T">One of the interfaces declared in <b>BizElements.Core.ProviderComponents</b> namespace.</typeparam>
        /// <returns>Component that implements the specified interface if supported by current provider.</returns>
        public static T GetComponent<T>(DbmsType dbms)
        {
            // Lazy init/load to prevent deadlocks because some component providers or even components depend on DbmsComponentFactory.
            // All providers in BizElements.Core are sealed and stateless classes, ie. they are inherently thread-safe.
            IDbmsComponentProvider provider;
            bool isInitialized = Providers.TryGetValue(dbms, out provider);
            if (!isInitialized)
            {
                provider = CreateProvider(dbms);
                Providers[dbms] = provider;
            }

            return provider.GetComponent<T>();
        }

        private static IDbmsComponentProvider CreateProvider(DbmsType dbms)
        {
            string dbmsName = dbms.ToString();
            string assemblyName, fullClassName;
            ProviderAssemblies.TryGetValue(dbmsName, out assemblyName);
            ProviderClasses.TryGetValue(dbmsName, out fullClassName);            
            if (string.IsNullOrEmpty(assemblyName) || string.IsNullOrEmpty(fullClassName))
                throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, Messages.DbmsComponentFactory_DbmsXIsNotSupported, dbms.ToString()), "dbms");

            return (IDbmsComponentProvider)ReflectionUtil.CreateInstance(assemblyName, fullClassName);
        }
    }
}